<template>
  <div ref="canvas"></div>
</template>

<script lang="ts" setup>
import * as THREE from "three";
import Stats from "three/examples/jsm/libs/stats.module";
import {GUI} from "three/examples/jsm/libs/lil-gui.module.min.js";
import {onMounted, ref} from "vue";
import {initCamera, initWebGLRenderer} from "@/book/ThreeUtils";
import {Easing, Tween} from "@tweenjs/tween.js";
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
import {PLYLoader} from "three/examples/jsm/loaders/PLYLoader";

const canvas = ref<any>();

const scene = new THREE.Scene();

const renderer = initWebGLRenderer(undefined);

const camera = initCamera({ x : 30, y : 30, z : 30});
camera.lookAt(0, 0, 0);

const orbitControl = new OrbitControls(camera, renderer.domElement);
orbitControl.update();

const stat = Stats();

const keyLight = new THREE.SpotLight(0xffffff);
keyLight.position.set(0, 80, 80);
keyLight.intensity = 2;
keyLight.lookAt(new THREE.Vector3(0, 15, 0));
keyLight.castShadow = true;
keyLight.shadow.mapSize.height = 4096;
keyLight.shadow.mapSize.width = 4096;
scene.add(keyLight);

const backlight1 = new THREE.SpotLight(0xaaaaaa);
backlight1.position.set(150, 40, -20);
backlight1.intensity = 0.5;
backlight1.lookAt(new THREE.Vector3(0, 15, 0));
scene.add(backlight1);

const backlight2 = new THREE.SpotLight(0xaaaaaa);
backlight2.position.set(-150, 40, -20);
backlight2.intensity = 0.5;
backlight2.lookAt(new THREE.Vector3(0, 15, 0));
scene.add(backlight2);

let group = null;
const loader = new PLYLoader();
loader.load(
    new URL("@/assets/models/carcloud/carcloud.ply", import.meta.url).href,
    (geom) => {
      const material = new THREE.PointsMaterial({
        color: 0xffffff,
        size: 1,
        opacity: 0.6,
        transparent: true,
        blending: THREE.AdditiveBlending,
        depthWrite: false,
        map: generateSprite()
      });

      // @ts-ignore
      geom.origPosition = geom.attributes['position'].clone();

      group = new THREE.Points(geom, material);
      group.scale.set(2.5, 2.5, 2.5);
      scene.add(group);
    }
)

const generateSprite = () => {
  const canvas = document.createElement('canvas');
  canvas.width = 16;
  canvas.height = 16;
  const context = canvas.getContext('2d');

  // draw the sprites
  const gradient = context.createRadialGradient(canvas.width / 2, canvas.height / 2, 0, canvas.width / 2, canvas.height / 2, canvas.width / 2);
  gradient.addColorStop(0, 'rgba(255,255,255,1)');
  gradient.addColorStop(0.2, 'rgba(0,255,255,1)');
  gradient.addColorStop(0.4, 'rgba(0,0,64,1)');
  gradient.addColorStop(1, 'rgba(0,0,0,1)');
  context.fillStyle = gradient;
  context.fillRect(0, 0, canvas.width, canvas.height);

  // create the texture
  const texture = new THREE.Texture(canvas);
  texture.needsUpdate = true;
  return texture;
};

const posSrc = { pos : 1 };
const tween = new Tween(posSrc).to({ pos : 0 }, 2000);
tween.easing(Easing.Bounce.InOut);

const tweenBack = new Tween(posSrc).to({ pos : 1 }, 2000)
tweenBack.easing(Easing.Bounce.InOut);

tweenBack.chain(tween);
tween.chain(tweenBack);

tween.start();

function updateMeshFunc(mesh) {
  tween.update();

  const positionArray = mesh.geometry.attributes['position']
  const origPosition = mesh.geometry.origPosition

  for (let i = 0; i < positionArray.count ; i++) {
    const oldPosX = origPosition.getX(i);
    const oldPosY = origPosition.getY(i);
    const oldPosZ = origPosition.getZ(i);
    positionArray.setX(i, oldPosX * posSrc.pos);
    positionArray.setY(i, oldPosY * posSrc.pos);
    positionArray.setZ(i, oldPosZ * posSrc.pos);
  }
  positionArray.needsUpdate = true;

  tweenBack.update();
}

const render = () => {
  stat.update();

  if (group) {
    updateMeshFunc(group);
  }

  requestAnimationFrame(render);
  renderer.render(scene, camera);
};

onMounted(() => {
  canvas.value.appendChild(stat.domElement);
  canvas.value.appendChild(renderer.domElement)
  render();
})


</script>
